<?php

/**
 * SigmaCMS - Content Management System
 *
 * Copyright (C) 2008 Pavol Biely
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    SigmaCMS
 * @author     Pavol Biely <pavolbiely@gmail.com>
 * @copyright  2008 Pavol Biely
 * @license    http://www.gnu.org/licenses/gpl.txt   GNU General Public License
 * @link       http://pabi3.com/
 * @since      0.1.0
 */

final class Element
{
	/**
	 * Quotes style
	 *
	 */
	const Q = "'";

	/**
	 * Empty elements
	 *
	 * @var array
	 */
	public $emptyElements = array('base', 'link', 'meta', 'hr', 'br', 'img', 'embed', 'param', 'area', 'col', 'input');

	/**
	 * Active element
	 *
	 * @var string
	 */
	private $_element;

	/**
	 * Child elements
	 *
	 * @var array
	 */
	private $_childs = array();

	/**
	 * Attributes of the active elements
	 *
	 * @var array
	 */
	private $_attributes = array();

	/**
	 * Factory
	 *
	 * @param integer|float|string|boolean $name
	 * @param string $content
	 * @return object
	 */
	static public function factory($name, $content = NULL)
	{
		if (!is_scalar($name)) {
			throw new Sigma_Exception('The first parameter is expected to be scalar, '.gettype($name).' given.');
		}

		$xml = new self();
		$xml->_element = $name;

		// add content
		if ($content !== NULL && is_scalar($content)) {
			$xml->content = $content;
		}

		return $xml;
	}

	/**
	 * Append a new element
	 *
	 * @param object|integer|float|string|boolean $element
	 * @param string $content
	 * @return object
	 */
	public function append($element, $content = NULL)
	{
		if ($element instanceof self) {
			$object = $this->_childs[][$element->_element] = $element;
		} else if (is_scalar($element)) {
			$object = $this->_childs[][$element] = self::factory($element);
		} else {
			throw new Sigma_Exception('Only scalar types and objects can be appended.');
		}

		// add content
		if ($content !== NULL && is_scalar($content)) {
			$object->content = $content;
		}

		return $object;
	}

	/**
	 * Overloading
	 *
	 * @param string $name
	 * @param string $value
	 */
	public function __set($name, $value)
	{
		// should an input be converted to entities or not
		$args = func_get_args();
		$noEntities = (boolean) @$args[2];

		if ($value === NULL) {
			// remove an attribute
			unset($this->_attributes[$name]);
		} else if ($noEntities) {
			// raw content
			$this->_attributes[$name] = (string) $value;
		} else {
			// content with entities
			$this->_attributes[$name] = _hsc((string) $value);
		}
	}

	/**
	 * Overloading
	 *
	 * @param string $name
	 * @param array $args
	 * @return object
	 */
	public function __call($name, array $args)
	{
		/**
		 * $args[0] - attribute's value
		 * $args[1]	-	HTML entities (true = no entities, false = convert to entites)
		 */
		$this->__set($name, @$args[0], @$args[1]);
		return $this;
	}

	/**
	 * Add new lines
	 *
	 * @return void
	 */
	private function newLine()
	{
		if (array_key_exists('nl', $this->_attributes)) {
			return str_repeat("\n", $this->_attributes['nl']);
		} else {
			return "\n";
		}
	}

	/**
	 * Return created elements
	 *
	 * @return string
	 */
	public function getContent()
	{
		// start tag
		$content = "<".$this->_element;

		// add attributes
		foreach ($this->_attributes as $name => $value) {
			if ($name == 'content' || $name == 'nl') {
				continue;
			}
			$content .= " "._hsc($name)."=".self::Q.$value.self::Q;
		}

		// is element empty or not
		if (in_array($this->_element, $this->emptyElements)) {
			// end tag
			$content .= " />";

			// add a new line
			$content .= $this->newLine();
		} else {
			// add content
			if (array_key_exists('content', $this->_attributes)) {
				// end starting tag
				$content .= ">".$this->_attributes['content'];
			} else {
				// end starting tag
				$content .= ">".$this->newLine();

				// add childs
				foreach ($this->_childs as $order => $elements) {
					foreach ($elements as $element => $child) {
						$content .= $child;
					}
				}
			}

			// end tag
			$content .= "</".$this->_element.">";

			// add a new line
			$content .= $this->newLine();
		}

		return (string) $content;
	}

	/**
	 * Print all elements
	 *
	 * @return string
	 */
	public function __toString()
	{
		return (string) $this->getContent();
	}
}
